#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <unistd.h>
#include "Mutex.hpp"
#include "Cond.hpp"
#include "Thread.hpp"
#include "Logger.hpp"
#include "Task.hpp"

const static int gthreadnumdefault = 3;

template <class T>
class ThreadPool
{
private:
    bool QueueIsEmpty()
    {
        return _q.empty();
    }
    void Routine(const std::string &name)
    {
        while (true)
        {
            T t;
            // 将来线程所要执行的函数：从任务队列中获取任务、将任务变为私有，执行该任务
            {
                LockGuard lockguard(&_lock);
                // 判断任务队列是否为空，为空就到条件变量下等待,并且线程池的状态要是运行的
                while (QueueIsEmpty() && _is_running) // 为了避免伪唤醒的情况，使用while 判断
                {
                    _thread_wait_num++;
                    _cond.Wait(_lock);
                    _thread_wait_num--;
                }
                if (_is_running == false && QueueIsEmpty())
                {
                    LOG(LogLevel::INFO) << name << " 任务队列为空&&线程池退出";
                    break;
                }
                // 此时任务队列中还有任务，继续让线程去执行就可以了
                // 从任务队列中获取任务，任务不要在临界区中执行
                t = _q.front();
                _q.pop();
            }
            t(); // 执行
            LOG(LogLevel::INFO) << name << "执行任务成功" << t.Result2String().c_str();
        }
    }

    ThreadPool(int threadnum = gthreadnumdefault)
        : _threadnum(threadnum), _thread_wait_num(0), _is_running(false)
    {
        // 创建线程对象放到 _threads 中
        for (int i = 0; i < _threadnum; i++)
        {
            std::string name = "thread-" + std::to_string(i + 1);
            Thread t([this](const std::string &name)
                     { this->Routine(name); }, name);

            _threads.push_back(t);
        }
        LOG(LogLevel::INFO) << "创建线程池对象成功";
    }

public:
    // 创建线程池
    void Start()
    {
        if (_is_running) // 避免重复创建
            return;
        _is_running = true;
        // 遍历_threads 中的所有线程调用Thread 中的Start
        for (auto &t : _threads)
        {
            t.Start();
        }
        LOG(LogLevel::INFO) << "创建线程池成功";
    }

    // 等待所有的线程
    void Wait()
    {
        for (auto &t : _threads)
        {
            t.Join();
        }
        LOG(LogLevel::INFO) << "等待线程池成功";
    }

    // 取消线程池
    void Stop()
    {
        if (_is_running == false)
            return;
        _is_running = false;
        // 需要将任务队列中的任务均执行完然后才能取消这个线程池
        // 如果条件变量下有等待的线程就将所有的线程唤醒
        if (_thread_wait_num)
        {
            _cond.NotifyAll();
        }
        // 然后在Routine 中处理线程退出的逻辑
    }

    // 将任务放入任务队列中
    void Enqueue(const T &in)
    {
        if (_is_running == false)
            return; // 取消线程不允许继续向任务队列中放任务
        {
            LockGuard lockguard(&_lock);
            _q.push(in);
            LOG(LogLevel::INFO) << "放入一个任务";

            // 唤醒线程
            if (_thread_wait_num)
                _cond.NotifyOne();
        }
    }

    // 获取单例
    // 首先需要将构造函数设置为私有的,并且禁掉拷贝构造与赋值重载
    static ThreadPool<T> *GetInstance()//静态成员方法无法直接使用非静态成员变量
    {
        //为了提高效率,使用双重判断
        if(_instance == nullptr)
        {
            LockGuard lockguard(&_sigletonlock);
            if (_instance == nullptr)
            {
                _instance = new ThreadPool<T>(6);
                LOG(LogLevel::INFO) << "线程池单例首次被使用,创建并初始化";
                //创建线程池
                _instance ->Start();
            }
        }
        return _instance;
    }

private:
    // 任务队列、线程池、线程池中的线程个数
    std::queue<T> _q;
    std::vector<Thread> _threads;
    int _threadnum; // 线程个数

    // 任务队列为临界资源需要保护
    Mutex _lock; // 将资源整体使用->一把锁
    Cond _cond;
    int _thread_wait_num; // 在条件变量下线程的等待数量

    bool _is_running;

    // 单例模式，只能创建一个ThreadPool 对象
    // 非静态成员变量可以使用静态函数
    static ThreadPool<T> *_instance;
    // 如果只创建一个线程池，那么线程池本身就是临界资源需要加锁
    static Mutex _sigletonlock;

};

//静态成员变量必须在全局初始化
template<class T>
ThreadPool<T>* ThreadPool<T>::_instance = nullptr;

template<class T>
Mutex ThreadPool<T>::_sigletonlock;